#include <os/debug.h>
#include <os/initcall.h>
#include <os/driver.h>
#include <driver/pcspeaker.h>
#include <lib/type.h>
#include <lib/string.h>
#include <sys/ioctl.h>
#include <sys/res.h>

static void BuzzerOn();
static void BuzzerOff();

// play sound
static void BuzzerOn()
{
    uint8_t tmp;

    tmp = In8((uint16_t)PPI_OUTPUT);
    if (tmp != (tmp | 3))
    {
        Out8((uint16_t)PPI_OUTPUT, (uint8_t)(tmp | 3));
    }
}

// stop sound
static void BuzzerOff()
{
    uint8_t tmp;

    tmp = In8((uint16_t)PPI_OUTPUT);
    Out8((uint16_t)PPI_OUTPUT, (uint16_t)(tmp & 0xfc));
}

// set beep freqence
static void BuzzerSetFreq(uint32_t frequence)
{
    if (frequence < 1)
        frequence = 1;

    if (frequence > 20000)
        frequence = 20000;

    uint32_t div = TIMER_FREQENCY / frequence;

    KPrint("[buzzer] pit timer2 set to %d\n", div);

    // set beep frequency
    InterruptDisable();
    Out8((uint16_t)PIT_CTRL, (uint8_t)PIT_MODE_COUNTER2 | PIT_MODE_MSB_LSB | PIT_MODE_3 | PIT_MODE_BINARY);
    Out8((uint16_t)PIT_COUNTER2, (uint8_t)(div));
    Out8((uint16_t)PIT_COUNTER2, (uint8_t)(div >> 8));
    InterruptEnable();
}

static iostatus_t BuzzerOpen(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    device_extension_t *extension = device->device_extension;

    if (!(extension->exist))
        status = IO_FAILED;

    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t BuzzerDevCtl(device_object_t *device, io_request_t *ioreq)
{
    uint32_t code = ioreq->parame.devctl.code;
    uint32_t arg = ioreq->parame.devctl.arg;
    iostatus_t status = IO_SUCCESS;

    switch (code)
    {
    case SOUNDIO_PLAY:
        BuzzerOn();
        break;
    case SOUNDIO_STOP:
        BuzzerOff();
        break;
    case SOUNDIO_SETVOL:
        BuzzerSetFreq(ioreq->parame.devctl.arg);
        break;
    default:
        status = IO_FAILED;
        break;
    }
    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t BuzzerClose(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;
    device_extension_t *extension = device->device_extension;

    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static void BuzzerWork()
{
    BuzzerOn();
    CpuDoDelay(10000);
    BuzzerOff();
}

static void BuzzerInit(device_extension_t *extension)
{
    extension->freqence = 2000;
    BuzzerSetFreq(2000);
    KPrint("[buzzer] set frequery to %d\n", extension->freqence);
    BuzzerWork();
}

static iostatus_t BuzzerWrite(device_object_t *device, io_request_t *ioreq)
{
    iostatus_t status = IO_SUCCESS;

    ioreq->io_status.status = status;
    ioreq->io_status.info = 0;
    IoCompleteRequest(ioreq);
    return status;
}

static iostatus_t BuzzerEnter(driver_object_t *driver)
{
    iostatus_t status;
    device_object_t *device;
    device_extension_t *extension;

    status = IoCreateDevice(driver, sizeof(device_extension_t), DEVICE_NAME, DEVICE_TYPE_BEEP, &device);
    if (status != IO_SUCCESS)
    {
        KPrint(PRINT_ERR "[driver] %s: create device failed!\n", __func__, DEVICE_NAME);
        return status;
    }

    // neight io mode
    device->flags = 0;
    extension = device->device_extension;
    extension->exist = 1;
    extension->open = 0;
    extension->status = 0;

    BuzzerInit(extension);
    return IO_SUCCESS;
}

static iostatus_t BuzzerExit(driver_object_t *driver)
{
    device_object_t *device, *next;

    list_traversal_all_owner_to_next_safe(device, next, &driver->device_list, list)
    {
        list_del_init(&device->list);
    }
    // delete driver name
    string_del(&driver->name);
    return IO_SUCCESS;
}

static iostatus_t BuzzerDriverFunc(driver_object_t *driver)
{
    driver->driver_enter = BuzzerEnter;
    driver->driver_exit = BuzzerExit;

    driver->dispatch_fun[IOREQ_OPEN] = BuzzerOpen;
    driver->dispatch_fun[IOREQ_CLOSE] = BuzzerClose;
    driver->dispatch_fun[IOREQ_WRITE] = BuzzerWrite;
    driver->dispatch_fun[IOREQ_DEVCTL] = BuzzerDevCtl;

    // set driver name
    string_new(&driver->name, DRIVER_NAME, DRIVER_NAME_LEN);
    return IO_SUCCESS;
}

static __init void BuzzerDriverEntry()
{
    KPrint("[driver] create buzzer driver\n");
    if (DriverObjectCreate(BuzzerDriverFunc) < 0)
    {
        KPrint("[driver] %s create driver failed!\n", __func__);
    }
}

driver_initcall(BuzzerDriverEntry);